﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace Dycox.Linq
{
    public class QueryProperty : PropertyDescriptor
    {
        private PropertyDescriptor _rawProperty;
        private QueryArgAttribute _attribute;
        private Type _itemsConverterType;

        public QueryProperty(PropertyDescriptor raw)
            : base(raw.Name, null)
        {
            _rawProperty = raw;

            _attribute = (QueryArgAttribute)raw.Attributes[typeof(QueryArgAttribute)];
            if (_attribute == null)
                throw new ArgumentException("Dycox.Data.queryArgAttribute not found on " + raw);

            InitializeStandardItems();
        }

        public bool IgnoreEmptyString
        {
            get { return _attribute.IgnoreEmptyString; }
        }

        public bool IgnoreDefaultValue
        {
            get { return _attribute.IgnoreDefaultValue; }
        }

        //public bool Exclusive
        //{
        //    get { return _attribute.Exclusive; }
        //}

        //public string Override
        //{
        //    get { return _attribute.Override; }
        //}

        protected override AttributeCollection CreateAttributeCollection()
        {
            List<Attribute> list = new List<Attribute>();
            AttributeCollection coll = _rawProperty.Attributes;
            TypeConverterAttribute tca = null;
            EditorAttribute ea = null;

            foreach (Attribute attr in coll)
            {
                if (attr is TypeConverterAttribute)
                {
                    tca = (TypeConverterAttribute)attr;
                    continue;
                }
                else if (attr is EditorAttribute)
                {
                    ea = (EditorAttribute)attr;
                    continue;
                }

                list.Add(attr);
            }

            if (_standardItemsSupport == StandardItemsSupport.None)
            {
                if (tca != null) list.Add(tca);
                if (ea != null) list.Add(ea);
            }
            else
            {
                System.Diagnostics.Debug.Assert(_itemsConverterType != null);
                list.Add(new TypeConverterAttribute(_itemsConverterType));
                Type test = Type.GetType("Dycox.Windows.Forms.Design.ListItemEditor, Dycox.Windows, Version=1.1.0.0, PublicKeyToken=862bfe4dede279ec");
                list.Add(new EditorAttribute("Dycox.Windows.Forms.Design.ListItemEditor, Dycox.Windows, Version=1.1.0.0, PublicKeyToken=862bfe4dede279ec", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"));
            }

            OnCreatingAttributeCollection(list);

            return new AttributeCollection(list.ToArray());
        }

        protected virtual void OnCreatingAttributeCollection(List<Attribute> list)
        {
        }

        public override bool CanResetValue(object component)
        {
            return _rawProperty.CanResetValue(component);
        }

        public override Type ComponentType
        {
            get { return _rawProperty.ComponentType; }
        }

        public override object GetValue(object component)
        {
            return _rawProperty.GetValue(component);
        }

        public override bool IsReadOnly
        {
            get { return _rawProperty.IsReadOnly; }
        }

        public override Type PropertyType
        {
            get { return _rawProperty.PropertyType; }
        }

        public override void ResetValue(object component)
        {
            _rawProperty.ResetValue(component);
        }

        public override void SetValue(object component, object value)
        {
            if (_standardItemsSupport != StandardItemsSupport.None && value != null)
            {
                if (_standardItemsSupport == StandardItemsSupport.Single)
                {
                    ConvertSingleValue(ref value);
                }
                else
                {
                    ICollection coll = value as ICollection;
                    if (coll == null)
                    {
                        if (value is IListItem)
                            coll = new object[] { value };
                        else
                            throw new NotSupportedException(SR.GetString("value_type_not_supported", value.GetType()));
                    }
                    int count = coll.Count;
                    if (count > 0)
                    {
                        ArrayList list = new ArrayList(count);
                        foreach (object obj in coll)
                        {
                            IListItem item = obj as IListItem;
                            if (item != null)
                            {
                                object v = item;
                                ConvertSingleValue(ref v);
                                list.Add(v);
                            }
                        }

                        value = list.ToArray(_elementType);

                    }
                    else
                        value = null;
                    
                }
            }
            _rawProperty.SetValue(component, value);
        }

        private void ConvertSingleValue(ref object value)
        {
            Type valueType = value.GetType();
            if (valueType != _elementType && !_elementType.IsAssignableFrom(valueType))
            {
                IListItem item = value as IListItem;
                if (item == null)
                    throw new ArgumentException();
                value = item.Value;
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            return _rawProperty.ShouldSerializeValue(component);
        }

        private bool _nullable;

        public bool Nullable
        {
            get { return _nullable; }
        }

        private Type _elementType;

        public Type ElementType
        {
            get { return _elementType; }
        }

        #region standard items manipulation

        private void InitializeStandardItems()
        {
            Type type = _rawProperty.PropertyType;

            if (type.IsValueType)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    _nullable = true;
                else
                    _nullable = false;
            }
            else
                _nullable = true;

            if (_attribute.EnableStandardItems)
            {
                TypeConverter tc = _rawProperty.Converter;

                if (type.IsArray)
                {
                    type = type.GetElementType();
                    _standardItemsSupport = StandardItemsSupport.Multiple;
                }
                else
                    _standardItemsSupport = StandardItemsSupport.Single;

                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    type = type.GetGenericArguments()[0];
                }

                _elementType = type;

                if (!(tc is ListItemConverter))
                {
                    if (type == typeof(bool))
                    {
                        _itemsConverterType = typeof(BooleanListItemConverter);
                    }
                    else if (type.IsEnum)
                    {
                        _itemsConverterType = typeof(EnumListItemConverter);
                    }
                    else
                        _itemsConverterType = typeof(ListItemConverter);
                }
                else
                    _itemsConverterType = tc.GetType();
            }

        }

        private StandardItemsSupport _standardItemsSupport;

        public StandardItemsSupport StandardItemsSupport
        {
            get { return _standardItemsSupport; }
        }


        #endregion


        public bool Suspended { get; set; }
    }
}
